package dan.frontend

import chisel3._
import chisel3.util._
import dan.common._
import dan.common.Consts._

trait HasBPUParam {
    val vaBits: Int = 32
    val fetchWidth: Int = 4
    val fetchBytes: Int = fetchWidth * 8
    val mixBits: Int = 24
    // 分支跳转通常是局部的,只存储部分目标地址
    val targetBits: Int = 15
    val rasSize: Int = 8
    // def mixHILO(pc: UInt): UInt = Cat(pc(vaBits - 1 , mixBits) , pc(mixBits - 1, 0) ^ pc(vaBits - 1 , vaBits - mixBits))
    def mixHILO(pc: UInt): UInt = pc
    // 取pc的高位(局部跳转通常不变) 拼接目标地址还原完整预测PC
    def getTargetPC(pc: UInt , target : UInt): UInt = {
        Cat(pc(vaBits - 1, targetBits + 2) , target(targetBits - 1 , 0) , 0.U(2.W))
    }
    def getTarget(targetPC: UInt): UInt = targetPC(targetBits+1, 2)
}

trait HasMicroBTBParam extends HasBPUParam{
    val wayNum = 4
    // PC[11:0]
    val tagView = 11
    // 用于存储tag的位数
    def tagBits = tagView + 1 - log2Ceil(fetchBytes)
    def getTag(pc: UInt): UInt = pc(tagView, log2Ceil(fetchBytes))
}

trait HasBTBParam extends HasBPUParam{
    val wayNum = 2
    val setNum = 64
    // TODO
    def setBits = log2Ceil(setNum)
    def getSetIdx(pc: UInt): UInt = pc(log2Ceil(fetchBytes) + setBits - 1, log2Ceil(fetchBytes))
    // PC[16:0]
    def tagView = 16
    def tagBits = tagView + 1 - log2Ceil(fetchBytes) - setBits
    def getTag(pc: UInt): UInt = pc(tagView, setBits + log2Ceil(fetchBytes))
}

trait HasBiMParam extends HasBPUParam{
    val setNum = 512
    val byPassNum = 2
    def changeState(cur: UInt, taken: Bool): UInt = {
        val isStrongTaken = cur === 3.U
        val isStrongNotTaken = cur === 0.U
        Mux(isStrongTaken && taken, 3.U,
        Mux(isStrongNotTaken && !taken, 0.U,
        Mux(taken, cur + 1.U, cur - 1.U)))
    }
}

trait HasLHPParam extends HasBPUParam{
    val historyLen = 13
    // Local History Table
    val lhtSize = 64
    // Pattern History Table (bim counters)
    val phtSize = 8192
    val lhtIdxBits = log2Ceil(lhtSize)
    val phtIdxBits = log2Ceil(phtSize)
    def changeState(cur: UInt, taken: Bool): UInt = {
        val extendState = Cat(0.U(1.W), cur)
        val nextStata = Mux(taken, extendState + 1.U, extendState - 1.U)
        Mux(nextStata(2), cur, nextStata(1, 0))
    }
    def getHashedIdx(pc: UInt, history: UInt): UInt = {
        history
    }
}

// TODO
// 只在前端使用的更新BPU的消息
class BPUMeta extends Bundle with HasBPUParam{
    val microBTBMeta = Output(new MicroBTBMeta())
    val btbMeta = Output(new BTBMeta())
    val bimMeta = Output(new BiMMeta())
    val lhpMeta = Output(new LHPMeta())
    // TODO 其他预测信息
}

class PredictionUpdate extends Bundle with HasBPUParam{
    val pc = UInt(vaBits.W)
    val brMask = UInt(fetchWidth.W)
    val taken = Bool()
    val mispredict = Bool()
    val isBr = Bool()
    val isJal = Bool()
    val isJalr = Bool()
    val target = UInt(vaBits.W)
    val idx = Valid(UInt(log2Ceil(fetchWidth).W))
    val meta = Vec(fetchWidth, new BPUMeta())
}

class BPUInfo extends Bundle with HasBPUParam{
    val predictedTarget =  Valid(UInt(targetBits.W))
    val takens = Output(Vec(fetchWidth, Bool()))
    val targets =  Vec(fetchWidth, Valid(UInt(targetBits.W)))
}
// 每个阶段分支预测器的输出
class BPUBundle extends Bundle with HasBPUParam{
    val pc = UInt(vaBits.W)
    val bpuInfo = new BPUInfo()
    val meta = Vec(fetchWidth, new BPUMeta())
}

class BPUReq extends CoreBundle{
    val pc = UInt(vaBits.W)
}

/* 
    后端发来的用于更新的消息
 */
class BrUpdateMask extends CoreBundle{
    val resolveMask = UInt(maxBrNum.W)
    val mispredictMask = UInt(maxBrNum.W)
}
class BrResolInfo extends CoreBundle{
    val valid = Bool()
    val uop = new UOp()
    val cfiType = UInt(CFI_BITS.W)
    val mispredict = Bool()
    val taken = Bool()
    val pcSel = UInt(PCSEL_BITS.W)
    val jalrTarget = UInt(xBits.W)
    val targetOffset = SInt(xBits.W)
}

class BrUpdateInfo extends CoreBundle{
    val updateMask = new BrUpdateMask()
    val resolInfo = new BrResolInfo()
}


